package com.scs.application.interceptor;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.utils.CacheUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.core.utils.UserUtils;
import com.scs.application.core.utils.UtilJavax;
import com.scs.application.modules.sys.adapter.FlowAdapter;
import com.scs.application.modules.sys.entity.FlowButton;
import com.scs.application.modules.sys.entity.FlowNode;
import com.scs.application.modules.sys.entity.Log;
import com.scs.application.modules.sys.enums.BusType;
import com.scs.application.modules.sys.enums.LogType;
import com.scs.application.modules.sys.service.LogService;
import com.scs.application.modules.sys.utils.UtilDatabase;
import com.scs.application.modules.wm.utils.ServiceState;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

/**
 *  控制层拦截器
 */
@Slf4j
public class ControllerInterceptor implements HandlerInterceptor {

    @Autowired
    private  JdbcTemplate jdbcTemplate;

    @Autowired
    protected LogService logService;
    @Autowired
    private TaskExecutor taskExecutor;

    @Autowired
    private FlowAdapter flowAdapter;

    @Override
    /**
     * @Description： 处理前置方法
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("ControllerInterceptor.preHandle");
        return this.doCheckData(request);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 清空ServiceState
        ServiceState.clear();
    }

    @Override
    /**
     * @Description： 处理后置方法
     */
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.debug("ControllerInterceptor.afterCompletion");
        log.debug("ControllerInterceptor.getServletPath {}", request.getServletPath());
        String businessException = response.getHeader("businessException");
        //请求类型  spdpc： pc端，spdapp：app端
        String requestType = request.getHeader("requestType");
        if(StringUtils.isBlank(requestType)) return;
        if (!requestType.equals("spdpc") && !requestType.equals("spdapp")) return;

        RequestWrapper requestWrapper = new RequestWrapper(request);
        String body = requestWrapper.getBody();

        if (StringUtils.isBlank(body)) return;
        if (!StringUtils.isJSONObject(body)) return;
        JSONObject obBody = JSONObject.parseObject(body);

        //操作表
        String interceptorOpTable = obBody.getString("interceptorOpTable");
        if (StringUtils.isBlank(interceptorOpTable)) return;
        //操作类型
        String interceptorOpType = obBody.getString("interceptorOpType");
        //操作主键名称
        String interceptorOpPkName = obBody.getString("interceptorOpPkName");
        //操作主键名称
        String interceptorOpViewName = obBody.getString("interceptorOpViewName");
        if (StringUtils.isBlank(interceptorOpType)) return;

        /**未抛出异常则说明处理成功，则新增需要保存的体制
         *  抛出异常的已经在【ExceptionHandleConfig】里面同一记录日志了，此处就不再记录了
         *  抛出异常后 ExceptionHandleConfig.businessExceptionHandle 增加了响应头参数 businessException = 1
         */
        switch (interceptorOpType) {
            case "dataviewdelete": //数据视图删除时候，因为是数组，所以需要特殊处理
                List<String> ids = (List<String>) obBody.get("ids");
                String userCode="未知",userName="未知";
                if (UserUtils.currentUser() != null) {
                    userCode=UserUtils.currentUser().getCode();
                    userName=UserUtils.currentUser().getName();
                }

                if (ex == null && StringUtils.isBlank(businessException)) {  //无异常，则表示处理成功
                    List<String> interceptorOpBusKeyArr = (List<String>) obBody.get("interceptorOpBusKeyArr");
                    dataviewAfterDelete(request.getServletPath(),UtilJavax.getIPAddress(request),interceptorOpBusKeyArr,interceptorOpViewName,interceptorOpTable,interceptorOpPkName,ids,userCode,userName);
                }else {
                    //清楚对应主键列表的 临时表插入语句
                    UtilDatabase.insertDbBackupExec(interceptorOpTable,interceptorOpPkName,ids,true);
                }
                break;
        }
    }

    /**
     * @Description： 数据视图删除后执行异步操作
     */
    private void dataviewAfterDelete(String action,String opIp, List<String> interceptorOpBusKeyArr,String interceptorOpViewName,String interceptorOpTable,String interceptorOpPkName,List<String> ids,String userCode,String userName) {
        taskExecutor.execute(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                //执行临时表插入语句
                List<Log> logSaveList = new ArrayList<Log>();
                for(int i = 0; i<ids.size() ; i ++ ){
                    //删除日志
                    Log log=new Log();
                    log.setMsg(interceptorOpViewName+"，"+action).setParam(String.format("%s = %s",interceptorOpPkName,ids.get(i))).setCreatorName(userName).setCreator(userCode);
                    log.setLogType(LogType.success).setBusType(BusType.OtherType.type02)
                            .setBusId(ids.get(i)).setBusKey(interceptorOpBusKeyArr.get(i)).setBusTable(interceptorOpTable)
                            .setIp(opIp)
                    ;
                    logSaveList.add(log);
                }
                logService.saveBatch(logSaveList);
                UtilDatabase.insertDbBackupExec(interceptorOpTable,interceptorOpPkName,ids,false);
            }
        });
    }

    /**处理前进行数据校验
     *  1、PC端、APP端 更新、删除的时候进行 数据数据是否过期判断
     */
    private boolean doCheckData(HttpServletRequest request) {
        //请求类型  spdpc： pc端，spdapp：app端
        String requestType = request.getHeader("requestType");
        if(StringUtils.isBlank(requestType)) return true;
        if (!requestType.equals("spdpc") && !requestType.equals("spdapp")) return true;

        RequestWrapper requestWrapper = new RequestWrapper(request);
        String body = requestWrapper.getBody();

        if (StringUtils.isBlank(body)) return true;

        String requestOpName = "save"; // 默认方法为保存
        String requestUri = request.getRequestURI();
        //需要排除的
        if (GlobalConsts.FLOW_CHECK_FILTER_EXCLUDE.contains(requestUri)){
            return true;
        }
        if (requestUri.indexOf("delete") > -1) { //删除操作
            requestOpName = "delete";
        } else if (requestUri.indexOf("flow_change") > -1) { //流程操作
            requestOpName = "flowChange";
        }

        if (StringUtils.isJSONObject(body)) {//JSON对象
            this.doCheckDataJsonObject(JSONObject.parseObject(body), requestOpName);
        }else if (StringUtils.isJSONArray(body)){ //JSON数组
            JSONArray arr = JSONArray.parseArray(body);
            for(int i=0; i<arr.size() ;i++)  {
                //只处理数组类
                if (!StringUtils.isJSONObject(arr.getString(i))) break;
                this.doCheckDataJsonObject(arr.getJSONObject(i), requestOpName);
            };
        };

        return true;
    }

    //校验JSON对象
    private void doCheckDataJsonObject(JSONObject obBody, String requestOpName) {
        //需要校验的流程标识
        String checkFlowCode = obBody.getString("checkFlowCode");
        if (StringUtils.isNotBlank(checkFlowCode)){
            //this.doCheckFlow(obBody, requestOpName);
        }

        //操作表
        String interceptorOpTable = obBody.getString("interceptorOpTable");
        if (StringUtils.isBlank(interceptorOpTable)) return;

        //操作类型
        String interceptorOpType = obBody.getString("interceptorOpType");
        //操作主键名称
        String interceptorOpPkName = obBody.getString("interceptorOpPkName");

        if ("dataviewdelete".equals(interceptorOpType)) { //数据视图删除
            List<String> ids = (List<String>) obBody.get("ids");
            String userName="未知";
            if (UserUtils.currentUser() != null) {
                userName=UserUtils.currentUser().getName();
            }
            //生成临时表插入语句
            UtilDatabase.insertDbBackupTmp(interceptorOpTable,interceptorOpPkName,ids,userName);
        } else if ("dataviewsave".equals(interceptorOpType)){ //数据视图保存

        }
    }

    //校验流程状态
    private void doCheckFlow(JSONObject obBody, String requestOpName) {
        //业务主表
        String checkFlowBusTable = obBody.getString("checkFlowBusTable");
        //业务主表id
        String checkFlowBusId = obBody.getString("checkFlowBusId");
        //前端传入状态
        final String checkFlowStatus = obBody.getString("checkFlowStatus");

        //业务主表ids 删除可能有批量操作
        List<String> checkFlowBusIdArr = (List<String>) obBody.get("checkFlowBusIds");

        //流程标识
        String checkFlowCode = obBody.getString("checkFlowCode");
        //操作名称
        String checkFlowOpTypeName = obBody.getString("checkFlowOpTypeName");
        if (StringUtils.isBlank(checkFlowBusTable)) return;
        if (StringUtils.isBlank(checkFlowBusId) && checkFlowBusIdArr == null) return;
        if (StringUtils.isBlank(checkFlowOpTypeName)) return;

        if (StringUtils.isNotBlank(checkFlowBusId)) {
            checkFlowBusIdArr = Arrays.asList(checkFlowBusId);
        }

        String interceptorOpTable = obBody.getString("interceptorOpTable");
        boolean isChildFlow = StringUtils.isNotBlank(interceptorOpTable) && !interceptorOpTable.equalsIgnoreCase(checkFlowBusTable);
        List<String> checkFlowStatusPassList = getFlowStatusPassList(checkFlowCode, requestOpName, isChildFlow, checkFlowStatus);
        if (checkFlowStatusPassList == null || checkFlowStatusPassList.isEmpty()) {
            throw new BusinessException("【%s】操作失败，请刷新重试<br>当前状态不可操作", checkFlowOpTypeName);
        }
        String timeStamp = UUID.randomUUID().toString();
        String flowStatus = timeStamp;
        Map mapSource = null;
        String busKeyField = "bus_key", busKey;
        for (int i = 0; i < checkFlowBusIdArr.size(); i++) {
            checkFlowBusId = checkFlowBusIdArr.get(i);
            mapSource = null;
            if (checkFlowBusTable.equals("dept_use")) busKeyField = "use_key";
            //不包含流程字段则忽略
            if (UtilDatabase.tableColumnsMap.get(checkFlowBusTable) == null || !UtilDatabase.tableColumnsMap.get(checkFlowBusTable).contains("flow_status"))
                return;

            //该表没有业务单号
            boolean hasBusKey = UtilDatabase.tableColumnsMap.get(checkFlowBusTable) != null && UtilDatabase.tableColumnsMap.get(checkFlowBusTable).contains(busKeyField);
            try {
                //不包含业务单据字段则忽略
                if (hasBusKey) {
                    mapSource = jdbcTemplate.queryForMap(String.format("select flow_status,%s from %s where id = %s", busKeyField, checkFlowBusTable, checkFlowBusId));
                } else {
                    mapSource = jdbcTemplate.queryForMap(String.format("select flow_status from %s where id = %s", checkFlowBusTable, checkFlowBusId));
                }
            } catch (EmptyResultDataAccessException e) {
            }
            if (mapSource == null) {
                throw new BusinessException("【%s】操作失败，请刷新重试<br>单据不存在或者已被删除<br>主键：%s", checkFlowOpTypeName, checkFlowBusId);
            }
            flowStatus = mapSource.get("flow_status") + "";
            if (hasBusKey) {
                busKey = "<br>单号：" + mapSource.get(busKeyField) + "";
            } else {
                busKey = "";
            }
            if (!requestOpName.equalsIgnoreCase("flowChange") && StringUtils.isNotBlank(checkFlowStatus) && !checkFlowStatus.equals(flowStatus)) {
                throw  new BusinessException("【%s】操作失败，请刷新重试<br>单据状态已发生改变", checkFlowOpTypeName);
            }
            if (StringUtils.isBlank(flowStatus)) return;
            if (!checkFlowStatusPassList.contains(flowStatus)) {
                String flowStatusText = "未知";
                if (GlobalConsts.FLOW_CLOSE_VALUE.equals(flowStatus)) {
                    flowStatusText = "已作废";
                } else {
                    final String finalFlowStatus = flowStatus;
                    List<FlowNode> flowNodes = (List<FlowNode>) CacheUtils.get(CacheUtils.FLOW_NODE_CACHE, checkFlowCode);
                    if (!flowNodes.isEmpty()) {
                        Optional<FlowNode> optFlowNode = flowNodes.stream().filter(flowNode1 -> flowNode1.getValue().equalsIgnoreCase(finalFlowStatus)).findFirst();
                        if (optFlowNode.isPresent()) {
                            flowStatusText = optFlowNode.get().getName();
                        }
                    }
                }

                throw new BusinessException("【%s】操作失败，请刷新重试<br>单据当前状态为【%s】不可进行该操作%s", checkFlowOpTypeName, flowStatusText, busKey);
            }
        }
    }

    /**
     *获取流程可操作状态列表
     * @param flowCode 流程标识
     * @param requestOpName 操作标识 save delete
     * @param childForm 是否是子流程
     * @return
     */
    private List<String> getFlowStatusPassList(String flowCode, String requestOpName, Boolean childForm, String checkFlowStatus) {
        List<FlowNode> flowNodes = (List<FlowNode>)CacheUtils.get(CacheUtils.FLOW_NODE_CACHE, flowCode); //先从缓存中获取
        if (flowNodes == null) {
            flowNodes = flowAdapter.getFlowNodeService(true).getByFlowCode(flowCode);
            if (flowNodes == null || flowNodes.isEmpty()) {
                return null;
            }
            CacheUtils.put(CacheUtils.FLOW_NODE_CACHE, flowCode, flowNodes);
        }

        if (requestOpName.equalsIgnoreCase("save")) { //保存的，获取当前状态为可添加或者可修改的
            if (childForm) {
                return flowNodes.stream().filter(flowNode -> flowNode.getFlagShowVbAdd()).map(FlowNode::getValue).collect(Collectors.toList());
            } else {
                return flowNodes.stream().filter(flowNode -> flowNode.getFlagShowVfbSave()).map(FlowNode::getValue).collect(Collectors.toList());
            }
        } else if (requestOpName.equalsIgnoreCase("delete")) { //删除的
            if (childForm) {
                return flowNodes.stream().filter(flowNode -> flowNode.getFlagShowVbDelete()).map(FlowNode::getValue).collect(Collectors.toList());
            } else {
                return flowNodes.stream().filter(flowNode -> flowNode.getFlagShowVfbDelete()).map(FlowNode::getValue).collect(Collectors.toList());
            }
        } else { //流程操作
            List<FlowButton> flowButtons = (List<FlowButton>)CacheUtils.get(CacheUtils.FLOW_BUTTON_CACHE, flowCode); //先从缓存中获取
            if (flowButtons == null) {
                flowButtons = flowAdapter.getFlowButtonService().getByFlowCode(flowCode);
                if (flowButtons == null || flowButtons.isEmpty()) {
                    return null;
                }
                CacheUtils.put(CacheUtils.FLOW_BUTTON_CACHE, flowCode, flowButtons);
            }
            return flowButtons.stream().filter(flowButton -> flowButton.getValueTo().equalsIgnoreCase(checkFlowStatus)).map(FlowButton::getValueIf).collect(Collectors.toList());
        }
    }
}
